#include <Arduino.h>
#include <QuickEspNow.h>
#include <Preferences.h> 
#include <Ticker.h>
//#include <PubSubClient.h>
#include <WiFi.h>
#include <esp_wifi.h>
#if defined ESP32C3MINI
#define RELAY_PIN 10 // 报警器信号线控制引脚
#define buttonPin 7 // 布防、撤防控制引脚 使用板载按钮
#define ALARM_PIN 1  //报警采集引脚
static const uint8_t ledPin = 8;
// 电量检测模块串口配置
#define UART_RX_PIN 20  // 串口接收
#define UART_TX_PIN 21  // 串口发送
#define BAUDRATE 9600   // 与电量检测模块的通信波特率
#elif defined ESP32
#define RELAY_PIN 27 // 报警器信号线控制引脚
#define buttonPin 0 // 布防、撤防控制引脚 使用板载按钮
#define ALARM_PIN 4  //报警采集引脚
static const uint8_t ledPin = 2;
#endif

// 电池参数 (根据实际电池调整)
const float FULL_VOLTAGE = 12.6f;  // 满电电压 
const float EMPTY_VOLTAGE = 8.5f; // 空电电压 
const float LOW_BATTERY_THRESHOLD = 15.0f; // 低电量阈值（百分之十五）

// 查询指令 (地址01)
const uint8_t QUERY_CMD[8] = {0x01, 0x04, 0x00, 0x01, 0x00, 0x01, 0x60, 0x0A};

// 全局变量
volatile float batteryVoltage = 0.0f;
volatile int batteryPercentage = 0;
bool lowBatteryFlag = false;
Ticker queryTicker;

static const uint8_t channel = 6; //通道
bool ALARM_Flag = false; // 定义布防撤防标志位，初始值为 false
bool ALARM_Battery_Flag = false; // 定义电池电量过低的撤防标志位，初始值为 false
Preferences preferences;   // NVS存储对象
uint8_t allowedRemote[6] = {0}; //存储网关的地址
bool hasStoredRemote = false;  //标记是否已经存储地址
uint16_t btDwonCount=0,btDwonMaxCount=50;//长按5秒则重新对码
int retry;                     //重发次数        
int max_retries=10;            //最大重发次数   
int retry_delay=50;            //重发时间间隔
bool lastAlarmState = LOW;     // 记录上一次报警状态

// 解析电压数据
bool parseVoltageData(uint8_t* data, int len) {
  if (len < 7) return false;
  
  // 基础校验：地址+功能码+数据长度
  if (data[0] != 0x01 || data[1] != 0x04 || data[2] != 0x02) {
    return false;
  }

  // 组合电压值 (大端序) 
  uint16_t rawVoltage = (data[3] << 8) | data[4];
  batteryVoltage = rawVoltage / 100.0f;
  
  // 计算电量百分比 (线性估算)
  batteryPercentage = static_cast<int>(
      constrain(
        map(batteryVoltage * 100, EMPTY_VOLTAGE * 100, FULL_VOLTAGE * 100, 0, 100),
        0, 100
      )
  );
  
  // 检查低电量状态
  bool newLowFlag = (batteryPercentage <= LOW_BATTERY_THRESHOLD);
  if (newLowFlag != lowBatteryFlag) {
    lowBatteryFlag = newLowFlag;
  }
  
  return true;
}

// 定时查询函数
void sendQuery() {
  Serial1.write(QUERY_CMD, sizeof(QUERY_CMD));
}

//发送方法
void sendcmd(char cmd){
  if(!hasStoredRemote){                //检查是否存储网关地址
    Serial.println("没有存储地址，发送失败");
    return;
  }
  comms_send_error_t result;    //发送结果的数据类型
  bool success = false;         //发送成功与否标志 
  
  for (int attempt = 0; attempt < max_retries; attempt++) {
    result = quickEspNow.send(allowedRemote, (uint8_t*)&cmd, 1);
    if (result == COMMS_SEND_OK) {
      Serial.printf("发送成功:%c\n",cmd);
      success = true;

      break;
    }
    Serial.printf("发送失败，错误码:%d, 重试:%d/%d\n", 
                 result, attempt+1, max_retries);
    delay(retry_delay);
  }
  
  if (!success) {
    Serial.printf("%d次尝试后仍然发送失败\n", max_retries);
  }
}

void batteryVoltageDate(){
  static uint8_t uartBuffer[32];
  static int bufferIndex = 0;
  //Serial.println("start");
  // 处理串口数据
  while (Serial1.available()) {
    uint8_t c = Serial1.read();
    
    // 帧头检测 (0x01)
    if (bufferIndex == 0 && c != 0x01) continue;
    
    // 存储数据
    uartBuffer[bufferIndex++] = c;
    
    // 完整帧检测 (7字节)
    if (bufferIndex >= 7) {
      if (parseVoltageData(uartBuffer, bufferIndex)) {
        Serial.printf("电压: %.1fV, 电量: %d%%\n", batteryVoltage, batteryPercentage);
      }
      bufferIndex = 0; // 重置缓冲区
    }
    
    // 防止缓冲区溢出
    if (bufferIndex >= sizeof(uartBuffer)) bufferIndex = 0;
  }

  // 处理低电量通知
  if (lowBatteryFlag) {
    ALARM_Battery_Flag = true;
    sendcmd('c'); //电量过低开启撤防
  //ALARM_Flag = false; //电量过低撤防
  //digitalWrite(ledPin, HIGH);
    Serial.println("低电量开启撤防!");
  }
  //digitalWrite(ledPin, LOW);
  ALARM_Battery_Flag = false;

}

void setBufeng(bool isYaoKong){
//过滤重复操作
/*if (ALARM_Flag==(digitalRead(RELAY_PIN)==HIGH)){
  Serial.println("重复操作！！");
  return;
  
}*/
if (ALARM_Flag) {
    // 布防
    if (isYaoKong)
      Serial.println("遥控布防");
    else{
      sendcmd('o');  //只有手动布防才同步布防命令
      Serial.println("手动布防");
    }
    /*digitalWrite(RELAY_PIN, HIGH);
    Serial.println("布防亮灯");
    delay(500);
    digitalWrite(RELAY_PIN, LOW);
    Serial.println("布防灭灯");*/
    digitalWrite(ledPin, LOW); 
  } else{
    // 撤防
    if (isYaoKong)
      Serial.println("遥控撤防");
    else{
      sendcmd('c');  //只有手动撤防才同步撤防命令
      Serial.println("手动撤防");
    }
      
    /*digitalWrite(RELAY_PIN, HIGH);
    Serial.println("第1次撤防亮灯");
    delay(500);
    digitalWrite(RELAY_PIN, LOW);
    Serial.println("第1次撤防灭灯");
    delay(1000);
    digitalWrite(RELAY_PIN, HIGH);
    Serial.println("第2次撤防亮灯");
    delay(500);
    digitalWrite(RELAY_PIN, LOW);
    Serial.println("第2次撤防灭灯");*/
    digitalWrite(ledPin, HIGH); 
  }
}

// 保存MAC地址到NVS（仅第一次有效）
void saveMacToNvs(uint8_t* mac) {
  // 检查是否已经存储过地址
  if (hasStoredRemote) {
    Serial.println("地址已存储，不再更新");
    return;
  }
  //将网关地址存储到NVS
  preferences.begin("pair_codes", false);            //打开命名空间
  preferences.putBytes("remote_mac", mac, 6);    //写入六位MAC地址
  preferences.end();                             //关闭命名空间 
  
  // 更新内存中的允许地址
  memcpy(allowedRemote, mac, 6);  
  hasStoredRemote = true;
  //打印存储信息
  Serial.printf("已永久存储允许的遥控器地址: %02X:%02X:%02X:%02X:%02X:%02X\n", 
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.println("此地址将永久有效且不可更改");
}


// 从NVS读取MAC地址
void readMacFromNvs() {
  uint8_t mac[6] = {0};
  preferences.begin("pair_codes", true);
  size_t len = preferences.getBytes("remote_mac", mac, 6);
  preferences.end();
  
  if (6 == len) {
    memcpy(allowedRemote, mac, 6);
    hasStoredRemote = true;
    Serial.printf("已加载永久存储的遥控器地址: %02X:%02X:%02X:%02X:%02X:%02X\n",
                  mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  } else {
    hasStoredRemote = false;
    Serial.println("未存储遥控器地址，等待首次配对");
  }
}

// 比较两个MAC地址是否相同
bool compareMac(uint8_t* mac1, uint8_t* mac2) {
  for (int i = 0; i < 6; i++) {
    if (mac1[i] != mac2[i]) return false;
  }
  return true;
}
/*接收ESPNOW数据，并处理接收到信息*/
void dataReceived(uint8_t *address, uint8_t *data, uint8_t len, signed int rssi,
                  bool broadcast) {
  if (len < 1)
    return;
  char cmd = data[0]; //创建足够大的缓冲区
  // 如果是新设备且尚未存储地址，则保存
  if (!hasStoredRemote) {
    saveMacToNvs(address);
  }else {// 如果已经存储了地址，只接受该地址的信号
    if (!compareMac(address, allowedRemote)) {
      Serial.printf("拒绝非授权的地址: %02X:%02X:%02X:%02X:%02X:%02X\n",
            address[0], address[1], address[2], address[3], address[4], address[5]);
      return;
    }
  }
  Serial.printf("从 %02X:%02X:%02X:%02X:%02X:%02X 接收到: %c (RSSI: %d)\n",
            address[0], address[1], address[2], address[3], address[4], address[5], cmd, rssi);
  if ('o' == cmd) {//布防
    if (!ALARM_Flag){
      ALARM_Flag = true;
      setBufeng(true);
    }else
      Serial.println("命令重复！！");
  } else if (cmd == 'c') { // 撤防
    if (ALARM_Flag){
      ALARM_Flag = false;
      setBufeng(true);
    }else
      Serial.println("命令重复！！");
  }else{
    Serial.printf("无法解析的命令%c\n",cmd);
    return;//其他命令则直接退出
  }
  
}

void setup() {
  Serial.begin(115200);
  Serial1.begin(BAUDRATE, SERIAL_8N1, UART_RX_PIN, UART_TX_PIN);//与电量检测模块通信
  // 设置继电器引脚为输出
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ALARM_PIN, INPUT_PULLDOWN); //初始下拉保持低电平
  digitalWrite(RELAY_PIN, LOW); // 默认关闭报警器
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH); 

  WiFi.disconnect(false, true);
  WiFi.mode(WIFI_STA);
  quickEspNow.onDataRcvd(dataReceived);

  // 初始化 ESP-NOW
  while (!quickEspNow.begin(channel, WIFI_IF_STA)) { // 指定频道
    Serial.println("ESP-NOW 初始化失败");
    delay(500);
  }
  Serial.println("ESP-NOW 初始化成功");
    // 读取存储的网关地址
  readMacFromNvs();
  
  Serial.println("设备就绪，等待信号...");

    // 启动定时查询电量检测模块电量
  queryTicker.attach_ms(500, sendQuery);
}

void loop() {

  //电量检测
  batteryVoltageDate();

  if (digitalRead(buttonPin) == LOW){
    //布防按钮按下时间计时，每个数字代表100毫秒
    btDwonCount=0;                                //按钮计数标志
    while (digitalRead(buttonPin) == LOW){        
      delay(100);                                 //按下按钮为低电平，开始按下之后每搁100ms，
                                                  //计数标志加1
      btDwonCount++;
    }
    //布防/撤防
    if (btDwonCount<btDwonMaxCount){              
      ALARM_Flag = !ALARM_Flag;                   //按下之后不到5s，改变布防撤防状态
      Serial.print("手动改变布防状态:");
      Serial.println(ALARM_Flag);
      setBufeng(false);
    }else{
      hasStoredRemote = false;                    //按下之后到了5s就标志为没有存储地址，进入对码模式
      Serial.print("进入重新对码模式！！！");       
    }
  }


  // 报警检查
  if ((digitalRead(ALARM_PIN) == HIGH)&&(ALARM_Flag == true)
       &&(ALARM_Battery_Flag == false)) {    //检测报警器
      // 切换输出引脚的状态
      // outputState = !outputState;
      // digitalWrite(RELAY_PIN, outputState);
      if (digitalRead(RELAY_PIN)!=HIGH){
        digitalWrite(RELAY_PIN, HIGH);                             //触发报警
        Serial.print("有报警触发\n");
        sendcmd('A');                                              //发送命令
        delay(100);
      }
    }else{
      // 切换输出引脚的状态
      // outputState = !outputState;  
      // digitalWrite(RELAY_PIN, outputState);
      if (digitalRead(RELAY_PIN)!=LOW){                   
        digitalWrite(RELAY_PIN, LOW);                              //停止报警
        Serial.print("报警解除\n");
        sendcmd('a');                                              //发送命令
        delay(100);
      }  
    }
}
